﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Xml;
using System.Windows.Forms;
using System.IO;
using System.Threading;
using MediaPortal.Configuration;
using SQLite.NET;
using MPMusicVids.Importer;
using MPMusicVids.Log;
using MPMusicVids.Data;

namespace MPMusicVids
{
    public partial class Rescan : Form
    {
        #region local variables
        private static Stack fileQueue;
        Stack<string> artistStack = new Stack<string>();
        private Logging log = Logging.getLogger();
        DataManager dm = MusicVidsMain.dm;
        delegate void SetTextCallback(string text);
        delegate void updatePanelCallback(UserControl UC);
        delegate void closeButtonCallback(bool enabled);
        delegate void ProgressCallback(int min, int max, int value);
        string extensions;
        ArtistSearch search = new ArtistSearch();
        bool stop = false;
        string[] threads = new string[4];
        int i = 0, max, current = 0;
        Hashtable artistLookup = new Hashtable();
        Hashtable parsedArtists = new Hashtable();
        #endregion

        public Rescan()
        {
            InitializeComponent();
            extensions = dm.getExtensions();
            doScan();
        }

        private void doScan()
        {
            try
            {
                //Ensure all directories are present.
                if (!System.IO.Directory.Exists(Config.GetFile(Config.Dir.Thumbs, "Music Vids\\Artists\\")))
                    Directory.CreateDirectory(Config.GetFile(Config.Dir.Thumbs, "Music Vids\\Artists\\"));
                if (!System.IO.Directory.Exists(Config.GetFile(Config.Dir.Thumbs, "Music Vids\\Thumbs\\")))
                    Directory.CreateDirectory(Config.GetFile(Config.Dir.Thumbs, "Music Vids\\Thumbs\\"));               
                //Load all files within watch folders, parsed with filedetails gathered.
                new Thread(new ThreadStart(loadFiles)).Start();
                button2.Enabled = false;
            }
            catch (Exception ex)
            {
                Logging log = Logging.getLogger();
                log.Append("Error Stacktrace: " + ex.StackTrace);
                log.Append("Error Message: " + ex.Message );
            }
        }

        private void readFiles() //Reads all files and adds to DB
        {
            while (fileQueue.Count != 0)
            {
                string[] fileDetails = (string[])fileQueue.Pop();
                //Process artist
                bool done = false;
                while (true)
                {
                    if (!dm.Contains(fileDetails[2]))
                        break;
                    else if (!(fileQueue.Count == 0))
                        fileDetails = (string[])fileQueue.Pop();
                        else
                        {
                            stop = true;                           
                            SetText("Nothing to do. Thread Stopped");
                            break;
                        }
                }
                if (stop)
                {
                    break;
                }
                while (!done)
                {
                    if (!artistStack.Contains(fileDetails[0]))
                    {
                        artistStack.Push(fileDetails[0]);
                        SetText("Searching Artist " + fileDetails[0]);
                        string[] artists = search.GetArtists(fileDetails[0]);
                        XmlDocument xmldoc = new XmlDocument();
                        while (!done)
                        {
                            if (artists.Length != 0)
                            {
                                if (artists[0].Split(';')[0].ToUpper() == fileDetails[0].ToUpper())
                                {
                                    //Perfect match, lets go ahead and add it.
                                    SetText("Matched artist " + fileDetails[0] + " grabbing details");
                                    string[] artistInfo = search.GetArtistInfo(artists[0].Split(';')[1], artists[0].Split(';')[0]);
                                    int artistIndex = dm.addArtist(artistInfo[0], artistInfo[1], artistInfo[2]);
                                    try
                                    {
                                        parsedArtists.Add(fileDetails[0], artistIndex);
                                    }
                                    catch (Exception e) { log.Append("ERROR in processing artist. Details: " + e.ToString()); }
                                    done = true;

                                }
                                else
                                {
                                    //First match wasn;t exact. Load up box to choose best match.
                                    SetText("Loading Possible matches for " + fileDetails[0]);
                                    ChooseArtist chooseArtist = new ChooseArtist(fileDetails[0], artists, true);
                                    updatePanel((UserControl)chooseArtist);
                                    while (chooseArtist.returning == "-1")
                                    { Thread.Sleep(1500); }
                                    string tmp = chooseArtist.returning;
                                    //If it comes back with a has, we need to re-search
                                    if (tmp.Substring(0, 1) == "#")
                                    {
                                        artists = search.GetArtists(tmp.Replace("#", ""));
                                        fileDetails[0] = tmp.Replace("#", "");
                                    }
                                    else
                                    {
                                        //Have chosen a match, select that index from results, and get artist info
                                        int m = int.Parse(tmp);
                                        string[] artistInfo = search.GetArtistInfo(artists[m].Split(';')[1], artists[m].Split(';')[0]);
                                        int artistIndex = dm.addArtist(artistInfo[0], artistInfo[1], artistInfo[2]);
                                        try
                                        {
                                            parsedArtists.Add(fileDetails[0], artistIndex);
                                        }
                                        catch (Exception e) { log.Append("ERROR in adding artist to DB. Details: " + e.ToString()); }
                                        done = true;
                                    }
                                }
                            }
                            else
                            {
                                //Last.FM Returned no matches for the artist parsed.
                                //Bring up box to search manually.
                                SetText("Loading Possible matches for " + fileDetails[0]);
                                ChooseArtist chooseArtist = new ChooseArtist(fileDetails[0], artists, false);
                                updatePanel((UserControl)chooseArtist);
                                while (chooseArtist.returning == "-1")
                                { Thread.Sleep(1500); }
                                string tmp = chooseArtist.returning;
                                if (tmp.Substring(0, 1) == "#")
                                {
                                    artists = search.GetArtists(tmp.Replace("#", ""));
                                    fileDetails[0] = tmp.Replace("#", "");
                                }
                            }
                        }
                    }
                    else
                    {
                        done = true;
                    }
                }
                //Process the song name
                int ArtistIndex = 0;
                if (!(parsedArtists.ContainsKey(fileDetails[0])))
                {
                    if (!(artistLookup.ContainsKey(fileDetails[0])))
                    {
                        fileQueue.Push(fileDetails);
                    }
                    else
                    {
                        try
                        {
                            ArtistIndex = (int)parsedArtists[artistLookup[fileDetails[0]]];
                            fileDetails[0] = (string)artistLookup[fileDetails[0]];
                        }
                        catch
                        {
                            fileQueue.Push(fileDetails);
                            log.Append("Something weird: " + fileDetails[0]);
                        }
                    }
                }
                else
                {
                    ArtistIndex = (int)parsedArtists[fileDetails[0]];
                }
                if (ArtistIndex != 0)
                {
                    SetText(String.Format("Grabbing thumbnail from file {0}", fileDetails[1]));
                    string imgFilename = Config.GetFile(Config.Dir.Thumbs, @"Music Vids\Thumbs\" + fileDetails[0] + " - " + fileDetails[1] + ".jpg");
                    string args = "-y -i \"" + fileDetails[2] + "\" -an -ss " + dm.getSeconds() + " -an -r 1 -vframes 1 -y -vcodec mjpeg -f mjpeg \"" + imgFilename + "\"";
                    System.Diagnostics.Process proc = new System.Diagnostics.Process();
                    proc.StartInfo.Arguments = args;
                    proc.StartInfo.FileName = dm.getFFMPEG();
                    proc.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
                    proc.Start();
                    Thread.Sleep(200);
                    SetText(String.Format("Adding {0} to database", fileDetails[1]));
                    dm.addSong(ArtistIndex.ToString(), fileDetails[2], fileDetails[1], "XXXX", imgFilename);
                    updateProgress(max, 0, current++);
                }
            }
            SetText("Thread has stopped.");
            threads[i++] = "DONE";
            if (threads[0] == "DONE" && threads[1] == "DONE" && threads[2] == "DONE" && threads[3] == "DONE")
            {
                SetText("All threads completed");
                closeButton(true);
            }
        }

        #region Delegates
        private void SetText(string text)
        {
            if (this.scanLog.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(SetText);

                this.Invoke(d, new object[] { text });
            }
            else
            {
                this.scanLog.Text += text + "\r\n";
                scanLog.SelectionStart = scanLog.Text.Length;
                scanLog.ScrollToCaret();
            }
        }

        private void updatePanel(UserControl UC)
        {
            if (this.pnlApproval.InvokeRequired)
            {
                updatePanelCallback d = new updatePanelCallback(updatePanel);
                this.Invoke(d, new object[] { UC });
            }
            else this.pnlApproval.Controls.Add(UC);
        }

        private void updateProgress(int max, int min, int value)
        {
            if (this.progress.InvokeRequired)
            {
                ProgressCallback d = new ProgressCallback(updateProgress);
                this.Invoke(d, new object[] { max, min, value });
            }
            else
            {
                this.progress.Maximum = max;
                this.progress.Minimum = min;
                this.progress.Value = value;
            }
        }

        private void closeButton(bool enabled)
        {
            if (this.pnlApproval.InvokeRequired)
            {
                closeButtonCallback d = new closeButtonCallback(closeButton);
                this.Invoke(d, new object[] { enabled });
            }
            else this.button2.Enabled = enabled;
        }
#endregion

        public void loadFiles() //Grabs all files from watch folders and parses out details
        {
            //Check for any missing files.
            string tmp = dm.cleanUp();
            SetText("Cleaning up: " + tmp);
            SetText("Loading files from watch folders. Please wait");
            log.Append("Running DB cleanup, Result: " + tmp);
            fileQueue = Stack.Synchronized( Scan.getStack() );
            object[] tmpArr = fileQueue.ToArray();
            max = 0;
            for (int x = 0; x < fileQueue.Count; x++)
            {
                string[] tmp1 = (string[])tmpArr[x];               
                if (!(dm.Contains(tmp1[2])))
                    max++;
            }
            tmp = "Rescan found " + fileQueue.Count + " files: " + max + " new files";
            updateProgress(max, 0, 0);
            log.Append(tmp);
            SetText(tmp);
            //Init 4 threads to scan all loaded files.
            for (int i = 0; i < 4; i++)
            {
                new Thread(new ThreadStart(readFiles)).Start();
                Thread.Sleep(1000);
            }
        }
        
        private void button1_Click(object sender, EventArgs e)
        {
            stop = true;
            SetText("User cancelled. Threads will stop at earliest opportunity");
        }

        private void button2_Click(object sender, EventArgs e)
        {
            this.Close();
        }
    }
}